/**
 * 
 */
package edu.caltech.android.database;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.database.sqlite.SQLiteException;
import android.util.Log;
import edu.caltech.android.sensor.accel.AccelSample;

/**
 * This is a singleton. Not thread-safe. The actual database is encapsulated in MeasurementDBAdapter
 * 
 * @author mfaulk
 * 
 */
public final class MeasurementDatabase {
	private static final String TAG = "MeasurementDatabase";
	private static MeasurementDatabase instance;

	private final int samplesPerLogFile;
	private final int maxLogFiles;
	private int numLogFiles = 0;
	private MeasurementDBAdapter dbAdapter;
	ArrayList<AccelSample> samples;

	/**
	 * 
	 * @param samplesPerLogfile
	 * @param maxLogFiles
	 *            - maximum number of log files. When full, oldest files will be
	 *            deleted.
	 * @param context
	 */
	private MeasurementDatabase(int samplesPerLogfile, int maxLogFiles,
			Context context) throws SQLiteException {
		if (samplesPerLogfile <= 0) {
			samplesPerLogfile = 1000;
		}
		this.samplesPerLogFile = samplesPerLogfile;
		this.maxLogFiles = maxLogFiles;
		dbAdapter = new MeasurementDBAdapter(context);
		dbAdapter.open();
		samples = new ArrayList<AccelSample>(samplesPerLogfile);
	}

	public static MeasurementDatabase getInstance(int samplesPerLogfile,
			int maxLogFiles, Context context) throws SQLiteException {
		if (instance == null) {
			instance = new MeasurementDatabase(samplesPerLogfile, maxLogFiles,
					context);
		}
		return instance;
	}

	/**
	 * Request that a sample be recorded to persistent storage
	 * 
	 * @param sample
	 * @throws IOException
	 * @throws IOException
	 */
	public void push(AccelSample sample) throws IOException {
		samples.add(sample);
		if (samples.size() >= samplesPerLogFile) {
			// write samples to file
			Log.v(TAG, "saving new logfile");
			String fileName = ExternalStorageManager.writeLogfile(samples);
			numLogFiles++;

			// get meta-data
			long startTime = samples.get(0).t;
			long endTime = samples.get(samples.size() - 1).t;

			dbAdapter.insert(fileName, startTime, endTime);
			samples.clear();

			// delete old files if necessary
			if (numLogFiles > maxLogFiles) {
				Log.v(TAG, "Deleting old logs.");

				List<String> oldestFiles = dbAdapter.getOldestLogfiles(1);
				for (String oldFile : oldestFiles) {
					Log.v(TAG, "deleting old file " + oldFile);
					int i = dbAdapter.delete(oldFile);
					Log.v(TAG, i + " rows to delete");
					ExternalStorageManager.deleteLogFile(oldFile);
					// TODO this is not robust. 
					numLogFiles--;
				}
			}
		}

	}

	/**
	 * Fetch data in all records which intersect the interval [startTime,
	 * endTime] This is potentially a problematic implementation, as it can
	 * return a very large amount of data (e.g. all data in the database)!
	 * 
	 * DEPRECATED: use getRecordFileNames instead
	 * 
	 * @param startTime
	 * @param endTime
	 * @return
	 * @deprecated
	 */
	public List<ArrayList<AccelSample>> getRecords(long startTime, long endTime) {
		ArrayList<ArrayList<AccelSample>> matchingRecords = new ArrayList<ArrayList<AccelSample>>();
		List<String> fileNames = dbAdapter.getIntersectingLogfiles(startTime,
				endTime);
		// inflate files
		for (String fileName : fileNames) {
			try {
				ArrayList<AccelSample> samples = ExternalStorageManager
						.readLogFile(fileName);
				matchingRecords.add(samples);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return matchingRecords;
	}
	
	/**
	 * 
	 * @param startTime
	 * @param endTime
	 * @return List of filenames of log files.
	 */
	public List<String> getRecordFileNames(long startTime, long endTime){
		return dbAdapter.getIntersectingLogfiles(startTime,	endTime);
	}
	
	
	/**
	 * Request that all pending data be persisted. This might be an issue: the
	 * database is only opened once, when the singleton instance is created.
	 * Calls to getInstance after a close would get an instance with a closed
	 * data base...
	 */
	public void close() {
		Log.d(TAG, "Closing database. TODO: flush pending data");
		dbAdapter.close();
	}
}
